/* Name: main.c
 * Project: KeyboardDecrypter
 * Author: Tom Gillbe
 * Orignal Author: Christian Starkjohann
 * Creation Date: 2006-02-02
 * Tabsize: 4
 * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH
 * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <util/delay.h>

#include "usbdrv/usbdrv.h"
#include "usbdrv/oddebug.h"

#define CLOCK_LOW		PINC |=  _BV(PC0)
#define CLOCK_RELEASE	PINC &= ~_BV(PC0)
#define DATA_LOW		PINC |=  _BV(PC1)
#define DATA_RELEASE	PINC &= ~_BV(PC1)

PROGMEM char usbHidReportDescriptor[35] = { /* USB report descriptor */
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0                           // END_COLLECTION
};

static void hardwareInit(void)
{
	uchar	i, j;

    DDRB = 0x0c;
	j = 0;
	while(--j){     /* USB Reset by device only required on Watchdog Reset */
		i = 0;
		while(--i); /* delay >10ms for USB reset */
	}
    DDRB = _BV(PB7); // LED output for desync
    DDRC = _BV(PC0) | _BV(PC1); // Configures the data / clock line controllers as output

    // Set up dropped character timer
    TIMSK0 = _BV(OCIE0A);
    OCR0A = 10;
}

/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */

static uchar    reportBuffer[2];    /* buffer for HID reports */
static uchar    idleRate;           /* in 4 ms units */

uchar	usbFunctionSetup(uchar data[8])
{
usbRequest_t    *rq = (void *)data;

    usbMsgPtr = reportBuffer;
    if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){    /* class request type */
        if(rq->bRequest == USBRQ_HID_GET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
            /* we only have one report type, so don't look at wValue */
            //buildReport(2/*keyPressed()*/);
            return sizeof(reportBuffer);
        }else if(rq->bRequest == USBRQ_HID_GET_IDLE){
            usbMsgPtr = &idleRate;
            return 1;
        }else if(rq->bRequest == USBRQ_HID_SET_IDLE){
            idleRate = rq->wValue.bytes[1];
        }
    }else{
        /* no vendor specific requests implemented */
    }
	return 0;
}

/* ------------------------------------------------------------------------- */

// Implemented from http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf
// using simple c# script
uchar keys[] = {0, 0x42, 0, 0x3E, 0x3C, 0x3A, 0x3B, 0x45, 0x68, 0x43, 0x41, 0x3F, 0x3D, 0x2B, 0x35, 0x67, 0x69, 0, 0, 0, 0, 0x14, 0x1E, 0, 0x6A, 0, 0x1D, 0x16, 0x04, 0x1A, 0x1F, 0, 0x6B, 0x06, 0x1B, 0x07, 0x08, 0x21, 0x20, 0, 0x6C, 0x2C, 0x19, 0x09, 0x17, 0x15, 0x22, 0, 0x6D, 0x11, 0x05, 0x0B, 0x0A, 0x1C, 0x23, 0, 0x6E, 0, 0x10, 0x0D, 0x18, 0x24, 0x25, 0, 0x6F, 0x36, 0x0E, 0x0C, 0x12, 0x27, 0x26, 0, 0x70, 0x37, 0x38, 0x0F, 0x33, 0x13, 0x2D, 0, 0x71, 0, 0x34, 0, 0x2F, 0x2E, 0, 0x72, 0x39, 0, 0x28, 0x30, 0, 0x31, 0, 0x73, 0, 0x64, 0, 0, 0, 0, 0x2A, 0, 0, 0x59, 0, 0x5C, 0x5F, 0, 0, 0, 0x62, 0x63, 0x5A, 0x5D, 0x5E, 0x60, 0x29, 0x53, 0x44, 0x57, 0x5B, 0x56, 0x55, 0x61, 0x47, 0, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
uchar keysExt[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x65, 0, 0, 0, 0, 0, 0, 0, 0x66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x4D, 0, 0x50, 0x4A, 0, 0, 0, 0x49, 0x4C, 0x51, 0, 0x4F, 0x52, 0, 0, 0, 0, 0x4E, 0, 0x46, 0x4B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

enum {
	CONTROL_LEFT = 1<<0,
	SHIFT_LEFT = 1<<1,
	ALT_LEFT = 1<<2,
	GUI_LEFT = 1<<3,
	CONTROL_RIGHT = 1<<4,
	SHIFT_RIGHT = 1<<5,
	ALT_RIGHT = 1<<6,
	GUI_RIGHT = 1<<7
} modifiers;
enum {
	RELEASE = 0x01,
	EXT0 = 0x02,
	EXT1 = 0x04,
} keystate;

volatile uint8_t newData = 0;
volatile uint8_t data = 0;
volatile uint8_t synced = 1;
volatile uint8_t state = 0;
volatile uint8_t send = 0;

int	main(void)
{
    hardwareInit();
	odDebugInit();
	usbInit();
	EICRA |= _BV(ISC11) | _BV(ISC10);
	EIMSK |= _BV(INT1);
	PORTB = 0;
	sei();
    DBG1(0x00, 0, 0);
    modifiers = 0;
	for(;;)
	{
		usbPoll();
		if (synced == 1 && newData)
		{
			newData = 0;
			if (data == 0xAA) // Correctly started up!
			{
				modifiers = 0;
				keystate = 0;
				continue;
			}
			// Watch for break keys, extended keys and keyboard mofifiers
			// (ctrl / alt / shft / GUI)
			// TODO Turn on lights on keypresses
			switch (data)
			{
			case 0xF0:
				keystate |= RELEASE;
				continue;
			case 0xE0:
				keystate |= EXT0;
				continue;
			case 0x14:
				if (keystate & EXT0)
					if (keystate & RELEASE)
					{
						modifiers &= ~CONTROL_RIGHT;
						keystate &= ~RELEASE;
					}
					else
						modifiers |= CONTROL_RIGHT;
				else
					if (keystate & RELEASE)
					{
						modifiers &= ~CONTROL_LEFT;
						keystate &= ~RELEASE;
					}
					else
						modifiers |= CONTROL_LEFT;
				break;
			case 0x11:
				if (keystate & EXT0)
					if (keystate & RELEASE)
					{
						modifiers &= ~ALT_RIGHT;
						keystate &= ~RELEASE;
					}
					else
						modifiers |= ALT_RIGHT;
				else
					if (keystate & RELEASE)
					{
						modifiers &= ~ALT_LEFT;
						keystate &= ~RELEASE;
					}
					else
						modifiers |= ALT_LEFT;
				break;
			case 0x12:
				if (keystate == RELEASE)
				{
					modifiers &= ~SHIFT_LEFT;
					keystate &= ~RELEASE;
				}
				else if (keystate == 0)
					modifiers |= SHIFT_LEFT;
				continue;
			case 0x1F:
				if (keystate & EXT0)
				{
					if (keystate & RELEASE)
					{
						modifiers &= ~GUI_LEFT;
						keystate &= ~RELEASE;
					}
					else
						modifiers |= GUI_LEFT;
				}
				break;
			case 0x27:
				if (keystate & EXT0)
				{
					if (keystate & RELEASE)
					{
						modifiers &= ~GUI_RIGHT;
						keystate &= ~RELEASE;
					}
					else
						modifiers |= GUI_RIGHT;
				}
				break;
			case 0x59:
				if (keystate == RELEASE)
				{
					modifiers &= ~SHIFT_RIGHT;
					keystate &= ~RELEASE;
				}
				else if (keystate == 0)
					modifiers |= SHIFT_RIGHT;
				break;
			}
        	reportBuffer[0] = modifiers;
        	if (keystate & RELEASE)
	        	reportBuffer[1] = 0;
        	else if (keystate & EXT0)
        		reportBuffer[1] = keysExt[data];
        	else
        		reportBuffer[1] = keys[data];

			while (!usbInterruptIsReady());
	        usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));

			keystate = 0;
		}
		else if (synced == 2)
		{
        	reportBuffer[0] = 0;
        	reportBuffer[1] = 0;
        	while(!usbInterruptIsReady());
        	usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));
			PORTB &= ~_BV(PB7); // Out of sync! :(
			synced = 1;
			//synced = 0;
		}
	}
	return 0;
}

void send_char(uchar c)
{
	state = 11;
	send = c;
	CLOCK_LOW;
	_delay_ms(0.11);
	DATA_LOW;
	asm("nop");
	asm("nop");
	asm("nop");
	asm("nop");
	asm("nop");
	//DATA_RELEASE;
	CLOCK_RELEASE;
	state = 12;
}

ISR(TIMER0_COMPA_vect)
{
    TCCR0B = 0;
	PORTB |= _BV(PB7); // Out of sync! :(
	state = 0;
}

uint8_t releaseKey = 0;

volatile uint8_t dataBuff;
volatile uint8_t paritiy = 0;

ISR(INT1_vect)
{
	// Small delay to give the PLD time to settle to a logic level after decryption
	asm("nop");
	asm("nop");
	asm("nop");
	uint8_t recvBit = !(PINA & _BV(PA0));
		if (state == 0) // Start receiving
		{
			if (recvBit)
			{
				PORTB |= _BV(PB7); // Out of sync! :(
				synced = 2;
			}
			state = 1;
			dataBuff = 0;
			paritiy = 0;
			// Timer to look for lost bits
			TCNT0 = 0;
			TCCR0B = _BV(CS02) | _BV(CS00);
		}
		else if (state >= 1 && state <= 8)
		{
			paritiy ^= recvBit;
			dataBuff |= (recvBit << (state - 1));
			state++;
		}
		else if (state == 9)
		{
			if (paritiy == recvBit)
			{
				PORTB |= _BV(PB7); // Out of sync! :(
				synced = 2;
			}
			state = 10;
		}
		else if (state == 10)
		{
			if (!recvBit)
			{
				PORTB |= _BV(PB7); // Out of sync! :(
				synced = 2;
			}
			data = dataBuff;
			newData = 1;
			state = 0;
			// Complted, disable error timer
			TCCR0B = 0;
		}
		else if (state == 11) // Start sending
		{
			paritiy = 0;
		}
		else if (state >= 12 && state <= 19)
		{
			if (send & (1 << (state - 12)))
			{
				DATA_RELEASE;
				paritiy ^= 1;
			}
			else
			{
				DATA_LOW;
				paritiy ^= 0;
			}
			state++;
		}
		else if (state == 20)
		{
			if (paritiy)
				DATA_LOW;
			else
				DATA_RELEASE;
			state++;
		}
		else if (state == 21)
		{
			DATA_RELEASE;
			state++;
		}
		else if (state == 22)
		{
			state = 0;
		}
}

/* ------------------------------------------------------------------------- */
